﻿using MindFusion.Diagramming.Wpf;
using MindFusion.Diagramming.Wpf.Layout;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Yuchen.NPA.Core.CompilerService.RegularExpression;
using Orientation = MindFusion.Diagramming.Wpf.Layout.Orientation;

namespace Yuchen.NPA.FAVisualizer
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private Rect bounds = new Rect(0, 0, 60, 22);

        private string _expression = "a(b|c)*";

        public event PropertyChangedEventHandler PropertyChanged;

        private void ChangeProperty(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public string Expression
        {
            get => _expression;
            set
            {
                _expression = value;
                ChangeProperty(nameof(Expression));
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
        {
            txtReg.DataContext = this;
        }

        void ParseToNFA()
        {
            var regexParser = new RegexParser(txtReg.Text);
            var state = regexParser.ToNFA();
            Draw(state);
        }

        void Draw(State state)
        {
            diagram.ClearAll();
            var sourceDiagramNode = diagram.Factory.CreateShapeNode(bounds);
            sourceDiagramNode.Text = state.Id;
            sourceDiagramNode.Id = state.Id;
            if (state.IsAccepted)
            {
                sourceDiagramNode.Text = "(" + sourceDiagramNode.Text + ")";
            }
            DrawNode(sourceDiagramNode, state);

            diagram.EndEdit(true);
            var layout = new LayeredLayout();

            layout.Anchoring = Anchoring.Reassign;
            layout.Orientation = Orientation.Horizontal;
            layout.EnforceLinkFlow = true;
            layout.StraightenLongLinks = true;
            layout.Arrange(diagram);
            diagram.ResizeToFitItems(5);
        }

        void DrawNode(DiagramNode sourceNode, State state)
        {
            foreach (var nextStateGroup in state.NextStates.GroupBy(x => x.Value))
            {
                var chars = string.Join(",", nextStateGroup.Select(x => x.Key.InputCharacter ?? 'ｅ'));
                var nextState = nextStateGroup.Key;
                var targetDiagramNode = diagram.Nodes.FirstOrDefault(x => x.Id?.ToString() == nextState.Id);
                if (targetDiagramNode == null)
                {
                    targetDiagramNode = diagram.Factory.CreateShapeNode(bounds);
                    targetDiagramNode.Text = nextState.Id;
                    targetDiagramNode.Id = nextState.Id;
                    if (nextState.IsAccepted)
                    {
                        targetDiagramNode.Text = "(" + targetDiagramNode.Text + ")";
                    }
                    var link = diagram.Factory.CreateDiagramLink(sourceNode, targetDiagramNode);
                    link.Dynamic = true;
                    link.Text = chars;
                    DrawNode(targetDiagramNode, nextState);
                    continue;
                }
                if (!diagram.Links.Any(x => x.Origin == sourceNode && x.Destination == targetDiagramNode && x.Text == chars))
                {
                    var link = diagram.Factory.CreateDiagramLink(sourceNode, targetDiagramNode);
                    link.Dynamic = true;
                    link.Text = chars;
                }

            }
        }

        private void TxtReg_OnKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                ParseToNFA();
            }
        }

        private void ToNFA_OnClick(object sender, RoutedEventArgs e)
        {
            ParseToNFA();
        }

        private void ToDFA_OnClick(object sender, RoutedEventArgs e)
        {
            var regexParser = new RegexParser(txtReg.Text);
            var state = regexParser.ToDFA();
            Draw(state);
        }

        private async void ToMinimizeDFA_OnClick(object sender, RoutedEventArgs e)
        {
            var text = txtReg.Text;
            var state = await Task.Factory.StartNew(() =>
               {
                   var regexParser = new RegexParser(text);
                   return regexParser.ToMinimizeDFA();
               });

            var map = new Dictionary<Core.CompilerService.RegularExpression.State, IDictionary<char, Core.CompilerService.RegularExpression.State>>();
            state.States.ForEach(s =>
                {
                    map.Add(s, s.NextStates.ToDictionary(x => x.Key.InputCharacter.Value, x => x.Value));
                });
            var file = File.OpenWrite(@"D:\dfa.bin");
            using (file)
            {
                new BinaryFormatter().Serialize(file, map);
                file.Flush();
            }
            Draw(state);

        }
    }
}
